package com.qxp.simplerpc.server;

import com.qxp.simplerpc.AbstractInvoker;
import com.qxp.simplerpc.annotation.Service;
import com.qxp.simplerpc.api.Invoker;
import com.qxp.simplerpc.config.ServerConfig;
import com.qxp.simplerpc.entity.InvokerMessage;
import com.qxp.simplerpc.exception.RpcException;
import com.qxp.simplerpc.utils.HystrixUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;

/**
 * RpcServer target method invoke
 */
@Component
@SuppressWarnings("unchecked")
public class RpcServerInvoker extends AbstractInvoker {
    // logger
    private static final Logger logger = LoggerFactory.getLogger(RpcServerInvoker.class);
    @Autowired
    private ServerConfig serverConfig;

    /**
     *
     * @return
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws NoSuchMethodException
     */
    @Override
    public  InvokerMessage invoke(InvokerMessage invokerMessage)  {
        try {
            long start = System.currentTimeMillis();

            Class target = invokerMessage.getInterfaceI();
            //Method method = target.getMethod(invokerMessage.getMethodName(), invokerMessage.getParameterTypes());
            Object instance = getInstance(target);
            //Object result = method.invoke(instance, invokerMessage.getArgs());
            HystrixUtils hystrixUtils = new HystrixUtils(instance,target,invokerMessage.getMethodName(),invokerMessage.getParameterTypes(),invokerMessage.getArgs());
            Object result = hystrixUtils.execute();
            addInvokerInfo(invokerMessage,instance,result);
            long executeSeconds = System.currentTimeMillis() - start;
            connect(invokerMessage,executeSeconds,true,null);
        }catch (Exception e){
            logger.error("method invoke error,",e);
            connect(invokerMessage,-1L,false,e);
        }
        return invokerMessage;
    }


    /**
     * 获取服务器开放的实例
     *
     * @return
     */
    private Object getInstance(Class<?> target) {
        Map<String,Object> beanMap = applicationContext.getBeansWithAnnotation(Service.class);
        // get bean by required bean interface
        if (!Objects.isNull(target)) {
            Object beanObject = this.applicationContext.getBean(target);
            if (Objects.isNull(beanObject) || !beanMap.containsValue(beanObject)) {
                throw new RpcException("the request bean impl can't be acquired...");
            }
            return beanObject;
        }
        throw new RpcException("rpc com.qxp.example get instace fail...");
    }

    private void addInvokerInfo(InvokerMessage invokerMessage, Object instance,Object result ){
        // setter
        invokerMessage.setClassName(instance.getClass().getName());
        invokerMessage.setServerHost(serverConfig.getLocalHost());
        invokerMessage.setServerPort(serverConfig.getServerPort());
        invokerMessage.setResult(result);
    }

    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
}
